import { AObject } from "./AObject";
import { AZone } from "./AZone";
import { ContainerType, RoutineResult, StatusType } from "./Definitions";
import type { ID } from "./ID";
import { HistoryEntry, N, Sentence } from "./NarrativeBase";
import { AAttributes, AClasses, Make } from "./Primitives";
import { TestStairwell } from "./TestSceneCorridor";
import { U } from "./Util";
import { World, forcePassTime, type ScheduledProcess } from "./WorldBase";

export let UnimplementedRoom = new AZone('[未实现的房间]', []);

export let TestRoomA = new AZone('测试间A', ['A']);
export let TestRoomB = new AZone('测试间B', ['B']);

TestRoomA.description = U.desc(
    ['一个宽敞的房间，唯一的光源是一扇小窗外苍白的天空，它从一面墙的高处照下来，在房间里的物体表面时只留下一些模糊的灰色颗粒和拉长的影子。在昏暗中你辨认出来，房间的墙壁和天花板都是深深的红色。'],
    ['一个宽敞、明亮的房间，墙壁和天花板都是鲜艳的红色；光似乎从墙壁的缝隙里面渗透出来。其中一面墙上有一个小门；另一面墙的高处有一扇小窗，窗外露出苍白的天空。'],
    ['你用手电筒照亮了一个宽敞、黑暗的房间；四面墙壁和天花板都是鲜艳的红色。手电筒的光柱外，唯一发亮的事物是一扇小窗外苍白的天空。']);
TestRoomA.temperature = 20;
TestRoomA.isLit = true;

let lever = new AObject('开关', ['拉杆', '东西'], {
    shortDescription: U.desc(['房间中央的地面上有一个很大的拉杆开关。']),
    description: (self, ag) => U.lambdaDesc(ag, [`一个金属质的大型拉杆开关，很牢固地连接在地上。开关处在${self.switchState ? '开' : '关'}的位置。`]),
    isSwitch: true,
    switchState: false,
    afterAction: {
        TurnOn: function* (self, ag) {
            TestRoomB.isLit = true;
            U.action(ag, N.text('传来一阵微弱的电子噪音。'));
            return RoutineResult.Ok();
        },
        TurnOff: function* (self, ag) {
            TestRoomB.isLit = false;
            U.action(ag, N.text('传来一阵微弱的电子噪音。'));
            return RoutineResult.Ok();
        }
    }
}, [AAttributes.Metal]);

let scale = new AObject('大型电子秤', ['电子秤', '秤', '仪器', '设备', '平台', '东西'], {
    indefiniteName: () => '一个大型电子秤',
    description: U.desc(['一个大型金属平台，用一组液压弹簧固定在地面上；它的侧面带有一个小的面板，上面有显示屏。']),
    containerType: ContainerType.Surface,
    mouthSize: 120,
    mass: 60,
    isSwitch: true,
    switchState: false,
    beforeAction: {
        TurnOn: function* (_, ag) {
            U.action(ag, N.text(ag, '在', scaleBoard, '旁边找到一个开关，按下了它。面板上的屏幕亮了起来。'));
            return RoutineResult.ContinueSilent();
        },
        TurnOff: function* (_, ag) {
            U.action(ag, N.text('面板上的屏幕熄灭了。'));
            return RoutineResult.ContinueSilent();
        }
    }
});
let scaleBoard = new AObject('电子秤的面板', ['面板', '屏幕', '显示屏', '东西'], {
    description: (_, ag) => U.lambdaDesc(ag, [scale.switchState ? `面板的屏幕上显示：*${(scale.totalMass - scale.mass).toFixed(3)}kg*` : `面板的屏幕上显示一片黑暗。`]),
});
scale.addPartInternal(scaleBoard);

let largeBox = new AObject('箱子', ['铁箱'], {
    indefiniteName: () => '一个大铁箱',
    containerType: ContainerType.Interior,
    isContentsVisible: true,
    isTakable: true,
    size: 50*30*20,
    diameter: 36,
    mouthSize: 20,
    mass: 2
}, [AAttributes.Metal]);
TestRoomA.addObjectInternal(largeBox);

let smallBoxes = [AAttributes.Blue, AAttributes.Green, AAttributes.Yellow].map(
    (attr: ID) => new AObject(`${attr.value}金属盒`, ['盒子', `${attr.value}盒子`], {
        indefiniteName: () => `一个${attr.value}的金属盒`,
        description: U.desc([`这是一个刷成${attr.value}的金属盒，盖子与盒体用铰链连接起来。`]),
        containerType: ContainerType.Interior,
        isContentsVisible: true,
        isTakable: true,
        size: 15*15*15,
        diameter: 19,
        mouthSize: 15,
        mass: 0.15,
        isOpenable: true,
        openState: false,
    }, [AAttributes.Metal, attr]));
largeBox.addContentInternal(...smallBoxes);
smallBoxes.forEach((x) => x.addContentInternal(
    ...[AAttributes.Red, AAttributes.Green, AAttributes.Blue].map(
        (attr: ID) => new AObject(
            `${attr.value}小罐`, ['罐子', '小罐子', `${attr.value}罐子`], 
    {
        indefiniteName: () => `一个${attr.value}的小罐子`,
        description: U.desc([`这是一个刷成${attr.value}的小罐子。`]),
        containerType: ContainerType.Interior,
        isContentsVisible: true,
        isTakable: true,
        size: 10*10*13,
        diameter: 14,
        mouthSize: 10,
        mass: 0.1,
        isOpenable: true,
        openState: false,
    }, [attr]))
));

let table = new AObject('桌子', ['书桌', '桌', '东西'], {
    indefiniteName: () => '一张桌子',
    shortDescription: U.desc(['房间一边有一张桌子。']),
    description: U.desc(['一张很普通的木质书桌。']),
    containerType: ContainerType.Surface,
    isHoldItem: true,
    isTakable: true,
    size: 100*100*100,
    diameter: 140,
    mouthSize: 90,
    mass: 20
}, [AAttributes.Wooden]);

let drawer = new AObject('抽屉', ['东西'], {
    shortDescription: (self, ag) => 
        U.lambdaDesc(ag, [`桌面下方有一个${self.openState ? '敞开' : '关着'}的抽屉。`]),
    isOpenable: true,
    openState: true,
    containerType: ContainerType.Interior,
    capacity: 40*40*15,
    mass: 0.5
}, [AAttributes.Wooden]);
table.addPartInternal(drawer);

let key = new AObject('银色钥匙', ['钥匙', '东西'], {
    indefiniteName: () => '一个银色钥匙',
    isTakable: true,
    size: 3*6*0.2,
    mass: 0.01,
    diameter: 3
}, [AAttributes.Metal], AClasses.Key);

let flashlight = new AObject('手电筒', ['手电', '东西'], {
    indefiniteName: () => '一个小手电筒',
    description: U.desc(
        ['虽然款式廉价，然而设计得十分周到，在黑暗中会发出一小圈荧光。'],
        ['一个闪亮的银色金属手电筒，是廉价的款式。']),
    isTakable: true,
    size: 3*3*15,
    mass: 0.06,
    diameter: 3,
    isSwitch: true,
    switchState: false,
    afterAction: {
        TurnOn: function*(self) {
            self.isLightSource = true;
            return RoutineResult.Continue();
        },
        TurnOff: function*(self) {
            self.isLightSource = false;
            return RoutineResult.Continue();
        }
    }
}, [AAttributes.Metal]);
drawer.addContentInternal(key, flashlight);

let window = new AObject('窗户', ['窗', '窗子', '窗口'], {
    description: U.desc(
        [`墙面高处的一扇小窗，从中露出一小块方形的天空。你越盯着看，越觉得这块灰白色区域似乎浮在其它所有昏暗的事物上面，略微颤动着。`],
        [`墙面高处的一扇小窗，从中露出一小块方形的天空。在光照下它显得很不起眼。`]),
    showInDescription: false,
    isReachable: false
}, [AAttributes.Glass]);

let sky = new AObject('天空', ['天'], {
    description: U.desc(['里面什么也没有。']),
    showInDescription: false,
    isReachable: false
});

let door = Make.door(TestRoomB, 15, '门', ['小门'], {
    linkedSide: () => door2,
    isLocked: true,
    afterAction: {
        Open: function* (self, ag) {
            if (TestRoomB.isLit && !TestRoomA.isLit)
                U.thought(ag, ['亮光从门外涌进来，但很快混入昏暗']);
            return RoutineResult.Continue();
        },
        Close: function* (self, ag) {
            if (TestRoomB.isLit && !TestRoomA.isLit)
                U.thought(ag, ['光线变得更暗了']);
            return RoutineResult.Continue();
        }
    }
});
Make.lockable(door, true, false, key);

TestRoomA.addObjectInternal(lever, scale, table, window, sky, door);
TestRoomA.exitDoor = () => door;

function* bombProcedure(): ScheduledProcess {
    const r1 = U.actionDurative(undefined, {
        inchoative: new Sentence(N.text('远处开始传来一种微弱的滴答声。')),
        cessative: new Sentence(N.text('')),
        progressive: new Sentence(N.text('远处继续传来微弱的滴答声。')),
        perfective: new Sentence(N.text('你听见远处传来一种微弱的滴答声。')),
    }).markGroup('tick').commence(TestRoomA);
    const r2 = U.actionDurative(undefined, {
        inchoative: new Sentence(N.text('有什么东西在附近开始滴答作响。')),
        cessative: new Sentence(N.text('')),
        progressive: new Sentence(N.text('有什么东西继续滴答作响：越来越响。')),
        perfective: new Sentence(N.text('你听见什么东西在滴答作响：越来越响。')),
    }).markGroup('tick').commence(TestRoomB);

    yield* forcePassTime(30);

    TestRoomA.status = () => null;
    TestRoomB.removeObjectInternal(bomb);
    TestRoomB.addObjectInternal(hole);
    let rep = '';
    if (vase.parentZone == TestRoomB) {
        rep = `花瓶震碎成了无数碎片。`;
        TestRoomB.addObjectInternal(brokenVase);
        vase.detachFromContainerInternal();
        for (let i of vase.contents) {
            i.detachFromContainerInternal();
            TestRoomB.addObjectInternal(i);
        }
    }

    r1.cease();
    r2.cease();
    yield World.schedule.issueInterrupt(3, undefined, 
        U.action(bomb, N.text(`一声巨响；${rep}地面摇晃了一下，传来什么地方东西坍塌的声音。`)), 
        [TestRoomB]);
    yield World.schedule.issueInterrupt(3, undefined, 
        U.action(bomb, N.text(`一声巨响；地面摇晃了一下，传来什么地方东西坍塌的声音。`)), 
        [TestRoomA]);
    return;
}

let button1 = Make.button('红色按钮', ['按钮'], 
    function* (self, ag) {
        if (!self.switchState) {
            self.switchState = true;
            World.schedule.addAction(bombProcedure(), self);
        } else {
            U.thought(ag, N.text('什么都没有发生。')).submitMomentary();
        }
        return RoutineResult.Ok();
    }, [AAttributes.Red]);

let button2 = Make.button('绿色按钮', ['按钮'], 
    function* (self, ag) {
        if (self.switchState) {
            U.thought(ag, N.text('什么都没有发生。')).submitMomentary();
            return RoutineResult.ContinueSilent();
        }
        self.switchState = true;
        U.thought(ag, N.SP(ag, '听到什么地方传来咔哒一声')).submitMomentary();
        World.schedule.addAction(function*() {
            yield* forcePassTime(1);
            yield World.schedule.issueInterrupt(3, undefined, U.action(self, N.text('房间的灯光突然闪了一下。', self.flags.has('pushed') ? '虽然不是第一次，但你还是吓了一跳；这里有某种很令人不安的感觉。' : '你吓了一跳。')), [TestRoomA]);
            self.flags.add('pushed');
            self.switchState = false;
        }(), self);
        return RoutineResult.ContinueSilent();
    }, [AAttributes.Green]);

let paper = new AObject('纸片', ['纸', '硬纸', '东西'], {
    indefiniteName: () => '一张硬纸片',
    isTakable: true,
    writing: {
        text: '',
        maxLength: 500,
        isStandardMedium: true,
        isInstrument: false,
        isEraser: false
    },
    mass: 0.005,
    diameter: 15,
    size: 1,
}, [AAttributes.Paper], AClasses.Paper);

let pencil = new AObject('自动铅笔', ['铅笔', '笔', '东西'], {
    indefiniteName: () => '一支自动铅笔',
    description: U.desc(['透明塑料制成的自动铅笔，一端附有橡皮。']),
    isTakable: true,
    writing: {
        text: '',
        maxLength: 0,
        isStandardMedium: false,
        isInstrument: true,
        isEraser: true
    },
    mass: 0.01,
    diameter: 0.7,
    size: 10,
}, [], AClasses.Pencil);

table.addContentInternal(button1, button2, paper, pencil);

// //////////

TestRoomB.description = (_, ag) => U.lambdaDesc(ag,
    [`一个宽敞的房间，光线很暗。这里看上去和测试间A类似，除了（你辨认出来）墙壁和天花板都是一种暗黄色。${door2.openState && TestRoomA.isLit ? '明亮的光线从小门外照进，但在昏暗中衰减得很快；此外，唯一的光源就是一扇小窗外苍白的天空。' : '唯一的光源是一扇小窗外苍白的天空，它从一面墙的高处照下来，在房间里的物体表面时只留下一些模糊的灰色颗粒和拉长的影子。'}`],
    ['一个宽敞、明亮的房间。光似乎从墙壁的缝隙里透出来；一面墙的高处有一扇小窗；另一面墙上有一扇小门；一切都和测试间A很像，除了墙壁和天花板涂成了一种鲜黄色。'],
    [`你用手电筒照亮了一个宽敞、黑暗的房间；它看上去和测试间A类似，但墙壁和天花板涂成了鲜黄色。${door2.openState && TestRoomA.isLit ? '门外照进来的些许亮光和' : ''}手电筒的光柱之外，唯一发亮的事物是一扇小窗外苍白的天空。`]);
TestRoomB.temperature = 15;
TestRoomB.isLit = false;

let clock = new AObject('电子钟', ['钟表', '钟', '表', '面板', '东西'], {
    shortDescription: (self, ag) => U.lambdaDesc(ag, 
        [`墙上嵌着的一个深色面板上显示出发微弱荧光的数字：*${U.timeString(World.schedule.absoluteTime)}*。`],
        [`墙上嵌着的一个深色面板上显示：*${U.timeString(World.schedule.absoluteTime)}*。`]),
    description: (self, ag) => U.lambdaDesc(ag, 
        [`一个光滑的深色面板，镶嵌在墙上；上面的文字发出微弱的蓝色荧光：*${U.timeString(World.schedule.absoluteTime)}*`],
        [`一个光滑的深色面板，镶嵌在墙上。上面显示着：*${U.timeString(World.schedule.absoluteTime)}*`]),
});

let bomb = new AObject('纸板包裹', ['小包裹', '包裹', '纸包', '纸板', '东西', '盒子', '盒', '包'], {
    shortDescription: U.desc(
        ['地板一角放着一团不起眼的东西，像一个小包裹。'],
        ['地板一角放着一团脏兮兮的东西，用棕色纸板包着，用细线杂乱地系成一个包裹。']),
    description: U.desc(
        ['你看不清楚这到底是什么东西。'],
        ['这是用细线和棕色纸板杂乱地做成的一个小包裹，看上去脏脏的。']),
    beforeAction: {
        Take: function* (_, ag) {
            U.thought(ag, N.SP(bomb, '被什么东西粘在地上，拿不起来')).submitMomentary();
            return RoutineResult.CustomError();
        }
    }
});

let hole = new AObject('洞', ['大洞', '东西'], {
    shortDescription: U.desc(
        ['地板一角有一个黑漆漆的洞。到处都是爆炸产生的碎屑。'],
        ['地板一角有一个大洞。到处都是爆炸产生的碎屑。']),
    description: (_, ag) => U.lambdaDesc(ag,
        [`地板一角，一个黑漆漆的洞${TestStairwell.isLit ? '；洞的深处隐隐地透出一点暗淡的暖色灯光' : ''}。周围，你只能看清一些杂乱无章、支棱在一起的形状；到处都是爆炸产生的碎屑。`],
        [`就在原先那个小包裹的地方，现在有一个大洞；${TestStairwell.isLit ? '某种暗淡的暖色灯光从洞的深处照上来，' : ''}你能看见台阶的形状，这些台阶原来被封死在了地板下面。周围是一圈似乎熔化了的黑糊糊的东西，其中夹杂着合成板材和金属支棱在一起的碎片；附近到处都是爆炸产生的碎屑。`]),
    isEnterable: true,
    zoneTo: () => [TestStairwell, 20],
    beforeAction: {
        Enter: function* (self, ag) {
            U.action(ag, N.text(ag, ag.hasLight ?  '摸索着踏出一只脚，' : '', `从洞边小心地踩上${TestStairwell.isLit ? '' : '黑暗的'}台阶，走了下去。`)).markImplicit().submitMomentary();
            U.thought(ag, N.text(ag, ag.hasLight ? `从洞边小心地踩上${TestStairwell.isLit ? '' : '黑暗的'}台阶，走下一段螺旋楼梯。` : '摸索着踏出一只脚，踩到了像是台阶的东西上。木板嘎吱作响，但是支撑住了你的重量。你小心地走下一段螺旋楼梯。')).submitMomentary();
            return RoutineResult.ContinueSilent();
        }
    }
});

let box = new AObject('小篮子', ['篮子', '东西'], {
    indefiniteName: () => '一个小篮子',
    isTakable: true,
    containerType: ContainerType.Interior,
    size: 20*30*15,
    mass: 0.2,
    diameter: 25,
    capacity: 20*30*15,
});

let vase = new AObject('花瓶', ['瓶子', '瓶', '东西'], {
    indefiniteName: () => '一个陶制细颈花瓶',
    isTakable: true,
    isHoldItem: true,
    containerType: ContainerType.Interior,
    size: 32*32*42,
    mass: 1,
    diameter: 25,
    capacity: 30*30*20,
    mouthSize: 5,
    isContentsVisible: false
});

let brokenVase = new AObject('破碎的花瓶', ['瓶子', '花瓶', '碎片', '东西'], {
    shortDescription: U.desc(['地上躺着一些陶制花瓶的碎片。']),
    isTakable: true,
    beforeAction: {
        Take: function* (_, ag) {
            U.thought(ag, N.SP(brokenVase, '已经没有用处了')).submitMomentary();
            return RoutineResult.CustomError();
        }
    }
});

let ball = new AObject('白色乒乓球', ['白色球', '球', '东西'], {
    indefiniteName: () => '一个白色的乒乓球',
    isTakable: true,
    size: 20,
    mass: 0.005,
    diameter: 3
}, [], AClasses.PingpongBall)
let ball2 = new AObject('蓝色乒乓球', ['蓝色球', '球', '东西'], {
    indefiniteName: () => '一个蓝色的乒乓球',
    isTakable: true,
    size: 20,
    mass: 0.005,
    diameter: 3
}, [], AClasses.PingpongBall)
vase.addContentInternal(ball, ball2);

let door2 = Make.door(TestRoomA, 15, '门', ['小门'], {
    linkedSide: () => door,
    afterAction: {
        Open: function* (_, ag) {
            if (!TestRoomB.isLit && TestRoomA.isLit)
                U.thought(ag, ['亮光从门外涌进来，但很快混入昏暗']).submitMomentary();
            return RoutineResult.Continue();
        },
        Close: function* (_, ag) {
            if (!TestRoomB.isLit && TestRoomA.isLit)
                U.thought(ag, ['光线变得更暗了']).submitMomentary();
            return RoutineResult.Continue();
        }
    }
});
Make.lockable(door2, true, true);

TestRoomB.addObjectInternal(clock, bomb, box, vase, door2, AObject.CloneFrom(sky), AObject.CloneFrom(window));
TestRoomB.exitDoor = () => door2;